# 守护进程

欢迎回到 `dora` 教程！我们探索了 `Dataflow`、 `节点` 和 `操作符` 等构建块，它们如何通过事件流接收信息，以及如何高效地传输 `数据消息/Arrow Data`。

我们还了解了 `API 绑定`如何帮助您编写组件代码，以及 `Dora CLI` 如何成为您管理一切的主要工具。

当您使用 `Dora CLI` 运行类似 `dora run` 命令时，实际上是在告诉 `dora` 执行您的数据流。但是，`CLI` 程序本身并不是运行节点和管理所有复杂通信的程序。
这项工作落到了构成 `dora` 运行时的后台进程身上。

这些后台进程中最重要的一个，尤其是在单机上运行的 **`Dora Daemon` 守护进程** 就是它。

## 本地交通经理

想象一下，你的计算机就像一个小型工厂车间 ，你的数据流描述了不同的工作站（ 节点 ）以及它们之间的传送带。你需要一个工头或经理来确保：

1. 正确的工作站已启动。
2. 他们获得了所需的材料（投入）。
3. 成品（输出）被快速移动到下一个工作站。
4. 它们遵循指令（例如关闭）。

`Dora Daemon` 就像那个当地的领班，它是一个运行在机器上的后台服务，负责管理特定机器上的 `dora` 组件。

其主要职责是：

- **启动节点：** 根据 `Dataflow YAML` 启动分配给其机器的节点进程。
- **管理本地通信：** 设置并处理在同一台机器上启动的节点之间的高性能通信通道（例如共享内存 ）。这对于速度至关重要！
- **接收指令：** 从 `Dora CLI` （在简单的本地设置中）或 `Dora Coordinator` （在分布式设置中）获取命令。
- **监控节点：** 跟踪其管理的节点的健康和状态。
- **处理流量：** 确保在本地节点之间移动的数据高效地到达正确的目标输入。在分布式设置中，它还有助于将数据路由到其他机器或从其他机器路由回来。

## 本地 `dora run` 中的守护进程
让我们考虑最简单的情况：使用 `dora run yolo.yml` 在单台机器上运行像我们的 `yolo.yml` 示例这样的数据流。

```bash
dora run yolo.yml
```

执行此命令时：

1. `Dora CLI` 程序启动。
2. `CLI` 连接到您机器后台运行的 `Dora` 守护进程。（如果守护进程未运行，`CLI` 可能会自动启动它）。
3. `CLI` 将 `yolo.yml` 数据流定义发送给守护进程。
4. 守护进程读取 `YAML` 并发现 `camera` 、 `object-detection` 和 `plot` 节点都需要在本地运行（因为没有指定 `_unstable_deploy: machine` 字段）。
5. 对于每个节点，守护进程都会在您的计算机上启动一个单独的进程，执行其 `path` 指定的代码（可能在运行 `build` 步骤之后）。
6. 根据 `YAML` 中定义的 `inputs` 和 `outputs` （例如 `image: camera/image` ），守护进程会在刚刚启动的进程之间建立高性能通信通道。对于图像等大数据，它使用共享内存 ，分配发送方和接收方 `Node` 进程均可直接访问的内存块，从而避免昂贵的复制操作。
7. 守护进程继续在后台运行，监视这些 `Node` 进程。
8. 当 `camera` 头节点发送输出 `image` 时，它会与守护进程通信（通过 `API` 绑定和内部通道）。守护进程会查看其内部映射（来自 `YAML`），并确定该 `image` 输出应该发送到 `object-detection` 的 `image` 输入和 `plot` 的 `image` 输入。
9. 如果图像数据较大，守护进程会确保将其放入共享内存中，并通过事件流通知 `object-detection` 和 `plot` 节点，告知其 `image` 输入端有新数据可用，并提供共享内存的详细信息。接收节点随后会直接从此共享内存块读取数据。
10. 守护进程继续管理此流量并监控节点。
11. 当你在运行 `dora run` 终端中按下 `Ctrl+C` 时，`CLI` 会向守护进程发送一个 `STOP` 信号。守护进程随后会向其管理的每个正在运行的 `Node` 进程的事件流发送 `STOP` 事件，使它们能够正常关闭。

以下是本地运行的简化序列图：

![daemon](/daemon01.png)

该图显示守护进程是其机器上运行的节点的控制和通信的中心点。

## 分布式数据流中的守护进程（简要）

在更复杂的设置中，您的数据流跨越多台机器，您将在每台承载 `dora` 节点的机器上运行一个 `Dora` 守护进程 。此外，还会有一个 `Dora 协调器`来管理这些机器上的整体数据流。

在这种情况下：

1. `Dora CLI` （通常）与 `Dora 协调器` （ `dora start yolo.yml --coordinator-addr ...` ）对话。
2. 协调器根据 `YAML` 中的 `_unstable_deploy: machine` 字段确定哪些节点在哪台机器上运行。
3. 协调器指示相关的守护进程 （每台机器上一个）启动分配给其机器的节点。
4. 然后，其各自机器上的每个守护进程执行如上所述的相同的本地管理任务：启动其指定的节点，在这些本地节点之间设置本地通信通道（如共享内存）并监视它们。
5. 对于不同机器上的节点之间的数据流动，守护进程会相互通信并与协调器（通常使用 `Zenoh`，如 `README` 中所述）进行通信，以确保数据在网络中路由。然而，守护进程的核心职责仍然是在其机器上本地管理 `dora` 环境。

## `Dora Daemon` 内部

`Dora Daemon` 作为单个后台进程运行（您经常可以在系统的进程列表中看到它）。它主要用 `Rust` 编写，以提高性能和可靠性。

其内部运作的关键方面包括：

- **通信通道：** 它使用操作系统提供的各种高效的进程间通信 (`IPC`) 机制与其启动的 `Node` 进程进行通信。这些通道是在 `Node` 使用 `API 绑定`连接到守护进程时建立的。
- **共享内存管理：** 守护进程负责分配和释放用于本地节点之间大数据消息的共享内存区域。它与丢弃令牌机制配合使用，在节点处理完共享内存块后接收通知，以便安全地重用或释放该内存块。
- **事件循环：** 与节点和操作符类似，守护进程拥有自己的内部事件循环。它监听不同类型的事件：
- 来自 `Dora CLI` 或 `Dora Coordinator` 的命令（例如，启动数据流、停止数据流、获取日志）。
- 来自 `Node` 进程的请求（例如，“我想发送数据”、“我正在订阅我的事件流”、“我已完成此 Drop Token”）。
- 来自操作系统的事件（例如，`Node` 进程已退出）。
- 与远程通信相关的事件（例如，通过 `Zenoh` 从另一台机器上的守护进程接收数据）。
- **状态管理：** 它跟踪哪些数据流在其机器上运行，哪些节点属于哪个数据流，每个节点的状态（运行、停止等）以及本地通信的输入/输出映射。

你可以在 `dora-daemon` 包中找到守护进程的主要逻辑，具体位置为 `binaries/daemon/src/lib.rs` 。此文件包含核心 `Daemon` 结构体及其 `run_inner` 方法，该方法实现了主事件循环，用于处理来自 `CLI/Coordinator`、`Node` 进程和其他来源的事件。

守护进程与 `Node` 进程之间的通信由 `DaemonRequest` （从 `Node` 到守护进程）和 `DaemonReply / NodeEvent / NodeDropEvent` （从守护进程到 Node）等消息定义，您可以在 `libraries/message/src/node_to_daemon.rs` 和 `libraries/message/src/daemon_to_node.rs` 文件中看到这些消息。每种语言的 `API` 绑定都封装了这些消息和底层通信通道。

## 总结

`Dora Daemon` 是 `dora` 运行时的关键组件。它充当机器上的本地管理器，负责启动和监控分配给它的 `Node` 进程，最重要的是，它基于 `Dataflow YAML` 建立和管理它们之间高效的本地通信通道（例如共享内存） 。它接收来自 `Dora CLI` 或 `Dora Coordinator` 的指令，并确保数据流量在其本地机器上正确流动。

现在我们了解了本地管理器（守护进程），让我们了解一下协调多个守护进程并管理跨不同机器的数据流的组件： **`Dora Coordinator 协调器`** 。
